from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.db import SessionLocal
from app.models.user import User
from passlib.context import CryptContext
from jose import jwt
from pydantic import BaseModel, field_validator
import re
from pathlib import Path
import pickle
from typing import Optional, Set

router = APIRouter()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"

# 识别项目根目录（.../backend/app/api/auth.py -> .../Recommendation）
ROOT_DIR = Path(__file__).resolve().parents[3]
DATA_PROCESSED_DIR = ROOT_DIR / "data" / "processed"
USER_ID_MAP_PATH = DATA_PROCESSED_DIR / "user_id_map.pkl"

_legacy_user_ids: Optional[Set[int]] = None

def _load_legacy_user_ids() -> Set[int]:
    global _legacy_user_ids
    if _legacy_user_ids is not None:
        return _legacy_user_ids
    ids: Set[int] = set()
    try:
        if USER_ID_MAP_PATH.exists():
            with open(USER_ID_MAP_PATH, "rb") as f:
                obj = pickle.load(f)
                # 常见结构：dict 原始 userId -> 索引，或反向。都尽量收集为 int 集合
                if isinstance(obj, dict):
                    for k, v in obj.items():
                        try:
                            if k is not None:
                                ids.add(int(k))
                        except Exception:
                            pass
                        try:
                            if v is not None:
                                ids.add(int(v))
                        except Exception:
                            pass
                elif isinstance(obj, (list, tuple, set)):
                    for x in obj:
                        try:
                            ids.add(int(x))
                        except Exception:
                            pass
    except Exception:
        # 读取失败时，保持为空集合
        ids = set()
    _legacy_user_ids = ids
    return _legacy_user_ids

def _is_legacy_user_id(uid: int) -> bool:
    # 优先用 user_id_map.pkl 判断；否则退回到历史范围 1..200948
    ids = _load_legacy_user_ids()
    if ids:
        return uid in ids
    return 1 <= uid <= 200_948

class RegisterRequest(BaseModel):
    # 用户输入的7位数字编号
    code: str
    # 登录密码，6位及以上
    password: str

    @field_validator("code")
    @classmethod
    def validate_code(cls, v: str):
        if not re.fullmatch(r"\d{7}", v or ""):
            raise ValueError("code must be exactly 7 digits")
        return v

    @field_validator("password")
    @classmethod
    def validate_password(cls, v: str):
        if not v or len(v) < 6:
            raise ValueError("password must be at least 6 characters")
        return v

class LoginRequest(BaseModel):
    # 登录支持 1-7 位编号（旧用户为 5 位，新用户为 7 位）
    code: str
    password: str

    @field_validator("code")
    @classmethod
    def validate_code(cls, v: str):
        if not re.fullmatch(r"\d{1,7}", v or ""):
            raise ValueError("code must be 1-7 digits")
        return v

    @field_validator("password")
    @classmethod
    def validate_password(cls, v: str):
        if not v or len(v) < 1:
            raise ValueError("password required")
        return v

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@router.post("/register")
def register(req: RegisterRequest, db: Session = Depends(get_db)):
    # 新规则：用户ID即为7位编号本身
    user_id_str = req.code
    user_id = int(user_id_str)
    # 用户名为：用户 + ID
    username = f"用户{user_id_str}"

    # 唯一性检查（ID 或 用户名已存在都视为已注册）
    if db.query(User).filter((User.id == user_id) | (User.username == username)).first():
        raise HTTPException(status_code=400, detail="该编号已注册，请直接登录或更换编号")

    hashed = pwd_context.hash(req.password)
    user = User(id=user_id, username=username, hashed_password=hashed)
    db.add(user)
    db.commit()
    # 不返回敏感信息
    return {"msg": "register success", "user_id": user_id, "username": username}

@router.post("/login")
def login(req: LoginRequest, db: Session = Depends(get_db)):
    # 新账号优先：ID = code（7位新账号）
    user = None
    user_new_id = None
    try:
        user_new_id = int(req.code)
        user = db.query(User).filter(User.id == user_new_id).first()
    except Exception:
        user = None

    # 旧账号回退：ID = code（系统里原有的 1..200948 用户）
    if user is None:
        try:
            legacy_id = int(req.code)
            user = db.query(User).filter(User.id == legacy_id).first()
        except Exception:
            user = None

    # 兼容用户名（极少数场景）：
    # 1) 新规则用户名：用户{code}
    if user is None:
        try:
            uname_new = f"用户{user_new_id}" if user_new_id is not None else None
            if uname_new:
                user = db.query(User).filter(User.username == uname_new).first()
        except Exception:
            pass
    # 2) 旧规则用户名：用户{3+code}
    if user is None:
        try:
            uname_old = f"用户3{req.code}"
            user = db.query(User).filter(User.username == uname_old).first()
        except Exception:
            pass

    # 仍未找到，则尝试将 code 识别为“老用户 ID”（依据 user_id_map.pkl 或历史范围），自动补全账号（免密码）
    if user is None:
        try:
            code_num = int(req.code)
            if _is_legacy_user_id(code_num):
                # 若同 ID 不存在，则创建一个最小化账号记录
                exists = db.query(User).filter(User.id == code_num).first()
                if not exists:
                    # 选择一个不冲突的用户名
                    uname_candidates = [f"用户{code_num}", f"用户3{req.code}", f"legacy_user_{code_num}"]
                    # 兼容 SQLAlchemy 返回 Row/tuple 的场景
                    taken = { (row[0] if isinstance(row, (list, tuple)) else getattr(row, 'username', row))
                              for row in db.query(User.username).all() }
                    username = next((u for u in uname_candidates if u not in taken), f"legacy_user_{code_num}")
                    user = User(id=code_num, username=username, hashed_password=None)
                    db.add(user)
                    db.commit()
                else:
                    user = exists
        except Exception:
            # 不是纯数字或其他异常，继续按无效凭据处理
            pass

    if user is None:
        raise HTTPException(status_code=401, detail="Invalid credentials")

    # 老用户：在 user_id_map.pkl 中或历史范围内 -> 免密码校验
    try:
        uid_int = int(user.id)
        is_legacy = _is_legacy_user_id(uid_int)
    except Exception:
        is_legacy = False
    if not is_legacy:
        if not pwd_context.verify(req.password, user.hashed_password):
            raise HTTPException(status_code=401, detail="Invalid credentials")
    token = jwt.encode({"user_id": user.id}, SECRET_KEY, algorithm=ALGORITHM)
    return {"access_token": token}